//Copyright (C) 2012 Chirila Alexandru
//This file is part of Mirror FTP
//
//Mirror FTP is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Mirror FTP is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with Mirror FTP.  If not, see <http://www.gnu.org/licenses/>.

#include "conn_cmd.h"

conn_cmd::conn_cmd(){}
conn_cmd::~conn_cmd(){}

int  conn_cmd::connected(){
	 ///Checks whether the control socket is connected, by sending an NOOP command.

     fd_set_blocking(sockfd,0);
     int err=send_cmd("NOOP","");
     fd_set_blocking(sockfd,1);
     return err==0;
}

int conn_cmd::closeC(){
	///Closes the connection

	close(sockfd);
	return 0;
}

int conn_cmd::fd_set_blocking(int fd, int blocking) {
	///Sets blocking or non-blocking mode (used for cleaning).
	///\param fd  socket file descriptor
	///\param blocking  0 - non blocking \n 1 - blocking

	int flags = fcntl(fd, F_GETFL, 0);
	if (flags == -1)
		return 0;
	if (blocking)
		flags &= ~O_NONBLOCK;
	else
		flags |= O_NONBLOCK;
	return fcntl(fd, F_SETFL, flags) != -1;
}

int conn_cmd::get_reply_int(string m){
	///Get first three numbers (reply) from server response.
	///\param m server response
	///\return the reply code

	int reply;
	string s;
	s=m.substr(0,3);
	reply=atoi(s.c_str());
	return reply;
}
int conn_cmd::establish(struct hostent server,int port){
	///Establishes a control connection with the server, at the specific port.
	///The port is automatically changed so it meets the hosts standard
	///\param server resolved server address
	///\param port the port on which it should connect
	///\return 0 in case of succes

	char buffer[1024];
	int reply;

	in_addr_t **addr_list;
	addr_list=(in_addr_t**)server.h_addr_list;


	portno=port;
	sockfd = socket(server.h_addrtype, SOCK_STREAM, 0); //create socket
	serv_addr.sin_family = server.h_addrtype;			//get server family from resolved host
	serv_addr.sin_addr.s_addr=*addr_list[0];			//get resolved server address
	serv_addr.sin_port = htons(portno);					//get portno

	if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0){	//establish connection
		perror("ERROR connecting\n");
		return errno;
	}

	socklen_t length=sizeof(serv_addr);
	getsockname(sockfd,(struct sockaddr *) &serv_addr,&length);
	address=inet_ntoa(serv_addr.sin_addr);

	bzero(buffer,1024);

	if (read(sockfd,buffer,1024)<0){
		perror("ERROR reading\n");
		return errno;
	}

	reply=get_reply_int(buffer);
	last_resp=buffer;

	if (reply/100==2){
		return 0;
	}
	else{
		return -3;
	}
}

int conn_cmd::read_more(){
	///Cleans the connection by trying to read more from socket (non-blocking mode).

	char buffer[1024];
	fd_set_blocking(sockfd,0);
	int ok=read(sockfd,buffer,1024);
	fd_set_blocking(sockfd,1);

	return (ok!=-1);
}

int conn_cmd::send_port(int port){
	///Makes a string containing the IP and port and sends to server.

	char buffer[1024];
	char *token;
	char temp[256];
	char addr[256];
	int len;
	const char delimiters[]= ".";

	bzero(addr,256);
	strcpy(addr,address.c_str());
	bzero(buffer,1024);

	//create IP string

	token = strtok(addr,delimiters);
	strcpy(buffer+strlen(buffer),token);
	strcpy(buffer+strlen(buffer),",");

	token = strtok(NULL,delimiters);
	strcpy(buffer+strlen(buffer),token);
	strcpy(buffer+strlen(buffer),",");

	token = strtok(NULL,delimiters);
	strcpy(buffer+strlen(buffer),token);
	strcpy(buffer+strlen(buffer),",");

	token = strtok(NULL,delimiters);
	strcpy(buffer+strlen(buffer),token);
	strcpy(buffer+strlen(buffer),",");

	//create port string

	len = snprintf(NULL, 0, "%d,%d",port/256,port%256);
	len = snprintf(temp, len+1, "%d,%d",port/256,port%256);

	strcpy(buffer+strlen(buffer),temp);
	send_cmd("PORT",buffer);

	return 0;
}

int conn_cmd::send_cmd(string command,string argument){
	///Sends a command to server and awaits a response to verify.
	///\param command the FTP command
	///\param argument the commands argument if it exists, else it should be an empty string ""
	///\return 0 in case of success

	char buffer[1024];
	int reply;

	read_more();

	bzero(buffer,1024);
	strcpy(buffer,command.c_str());

	if (argument.length()!=0){
		strcpy(buffer+strlen(buffer)," ");
		strcpy(buffer+strlen(buffer),argument.c_str());
	}
	strcpy(buffer+strlen(buffer),"\r\n");

	if (write(sockfd,buffer,strlen(buffer))<0){
		perror("ERROR sending command\n");
		return errno;
	}

	bzero(buffer,1024);
	if (read(sockfd,buffer,1023) < 0){
		perror("ERROR reading response\n");
		return errno;
	}

	last_resp=buffer;
	reply=get_reply_int(buffer);
	read_more();

	if ((reply/100==2) || (reply/100==3) || (reply/100==1))
		return 0;
	else
		return -3;
}

int conn_cmd::login(string user,string pass){
	///Logs into the FTP server with the given username and password.
	///\return 0 in case of success

	if (this->send_cmd("USER",user)==0){
		if (this->send_cmd("PASS",pass)==0){
			return 0;
		}
		else{
			return -2;
		}
	}
	else{
		return -1;
	}
}
